using System;
using System.Collections.Generic;
using System.Text;
using RikMigrations.Providers;
using System.ComponentModel;

namespace RikMigrations
{
	public interface IMigration
	{
		void Up(DbProvider db);
		void Down(DbProvider db);
	}

    [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = true)]
	public class MigrationAttribute : Attribute, IComparable<MigrationAttribute>
	{
		
		#region [rgn] Fields (3)

		private string moduleName;
		private Type type;
		private int version;

		#endregion [rgn]

		#region [rgn] Constructors (3)

		public MigrationAttribute(Type type, int version, string moduleName)
		{
			this.type = type;
			this.version = version;
			this.moduleName = moduleName;
		}
		
		public MigrationAttribute(Type type, int version)
		{
			this.type = type;
			this.version = version;
		}
		
		/// <summary>
        /// Only use this on a class not on an Assembly Attribute
        /// </summary>
        /// <param name="version"></param>
        public MigrationAttribute(int version)
        {
            this.version = version;
        }
		
		#endregion [rgn]

		#region [rgn] Properties (3)

		public string ModuleName
		{
			get { return moduleName ?? MigrationManager.DefaultModuleName; }
			set { moduleName = value; }
		}
		
		public Type Type
		{
			get { return type; }
            set { type = value; }
		}
		
		public int Version
		{
			get { return version; }
		}
		
		#endregion [rgn]

		#region [rgn] Methods (3)

		// [rgn] Public Methods (2)

		public void Down(DbProvider db)
		{
            runMigration(db, delegate(IMigration migration)
            {
                Log.WriteLine("Downgrading from version {0} (Module: {1})", Version, ModuleName);
                migration.Down(db);
            });
		}
		
		public void Up(DbProvider db)
		{
            runMigration(db, delegate(IMigration migration)
            {
                Log.WriteLine("Upgrading to version {0} (Module: {1})", Version, ModuleName);
                migration.Up(db);
            });
		}
		
		// [rgn] Private Methods (1)

		private void runMigration(DbProvider db, Action<IMigration> migrationToRun)
        {
            db.CreateTransaction();
            try
            {
                migrationToRun.Invoke(createInstanceOfMigration());
                db.CommitTransaction();
            }
            catch
            {
                db.RollbackTransaction();
                throw;
            }
        }

        private IMigration createInstanceOfMigration()
        {
            IMigration migration = Activator.CreateInstance(Type) as IMigration;
            if (migration == null)
                throw new InvalidOperationException("Migration type does not implement IMigration - " + Type.Name);
            return migration;
        }

        #endregion [rgn]

		#region IComparable<MigrationAttribute> Members
		public int CompareTo(MigrationAttribute that)
		{
			return version.CompareTo(that.version);
		}
		#endregion
	}
}